/* 
 * File:   Edificacion.cpp
 * Author: cesar
 * 
 * Este archivo contiene la implementacion de la clase Edificacion.hpp y sus metodos.
 */

#include "Edificacion.hpp"
#include "time.h"
#include "Terreno.hpp"
#define ZINI -1500          //Punto inicial de aparicion de los edificios
#define LIM_HORIZONTAL 150 // Espacio factible para la aparicion de edificios en el eje X
#define ZLIM 50            // Posición en Z donde desaparece el edificio de la escena
#define IMPACT_RATIO 2.0   // Tolerancia de impacto de la nave con los edificios


int Edificacion::seed = time(NULL); //semilla para la generacion aleatoria de

/**
 * Constructor de la clase Edificacion. Esta permite generar un edificio con
 * dimensiones aleatorias
 * 
 * @param nombre_tex : El nombre de la textura.
 * @param n : Un apuntador a un objeto del tipo Nave
 * @param o : El offset o desplazamiento desde el punto inicial.
 */
Edificacion::Edificacion(char * nombre_tex, Nave * n, int o) : ObjetoGrafico(0.0, SUELO, 0.0) {

    nave = n;
    nombre_textura = nombre_tex;
    offset = o;
    //colocarle dimensiones aleatorias
    randomSize();
    
    //colocar posicion aleatoria
    randomPos();
    //reflexion ambiental del material
    material_ambient[0] = 0.3;
    material_ambient[1] = 0.3;
    material_ambient[2] = 0.3;

    //reflexion difusa del material
    material_difuso[0] = 1.0;
    material_difuso[1] = 1.0;
    material_difuso[2] = 1.0;


    //reflexion especular del material
    material_especular[0] = 0.0;
    material_especular[1] = 0.0;
    material_especular[2] = 0.0;

    //emision de luz del material
    material_emision[0] = 0.0;
    material_emision[1] = 0.0;
    material_emision[2] = 0.0;

    //brillo
    material_brillo[0] = 0.0;

    //generar las texturas
    glGenTextures(1, &textura);
    glBindTexture(GL_TEXTURE_2D, textura);
    
    image = glmReadPPM(nombre_textura, &iwidth, &iheight);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iwidth, iheight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

    //cargar sonido de choque contra edificacion
    cargarSonido("choque.wav", false);


}

/**
 *  Este metodo permite dibujar la edificacion en la escena.
 */
void Edificacion::dibujar() {

    /**************DIBUJANDO LA SOMBRA******************/
    
    glPushMatrix();
    glLoadIdentity();
    
    // activamos el modo sombra para que lo que dibujemos se proyecte contra el
    //plano
    activarModoSombra();

    
    //Ajustamos la sombra un poco para obtener mejores efectos
    glTranslated(x, y, z - profundidad / 10);
    
    //dibujamos el edificio sin las texturas
    dibujarEdificio_ST();

    //devolvemos los parametros a la normalidad
    desactivarModoSombra();


    glPopMatrix();

    /**************DIBUJANDO EL EDIFICION******************/
    glPushMatrix();

    //activar y reconfigurar la textura
    glEnable(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, textura);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iwidth, iheight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    //configurando los materiales
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_difuso);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_especular);
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_emision);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, material_brillo);

    glTranslated(x, y, z);
   
    dibujarEdificio();



    glDisable(GL_TEXTURE_2D);
    glPopMatrix();






}



/**
 * Metodo idle que permite realizar los calculos de movimiento y colisiones
 * del edificio.
 * 
 * @param s : El frameskip actual.
 */
void Edificacion::idle(int s) {
    
    //desplazar
    z += s;
    
    bool cond1, cond2, cond3;


    if (z < 0) {
        
        list<Laser>::iterator it = nave->disparos.begin();

        while (it != nave->disparos.end()) {
            if ((*it).activado()) {

                //condiciones de colision
                cond1 = abs(x - (*it).getX())<(ancho / 2);
                cond2 = abs((*it).getY() - y - SUELO) < alto;
                cond3 = abs(z - (*it).getZ())<(profundidad / 2);

                if (cond1 && cond2 && cond3) {
                    /* Colision laser vs Edificacion*/
                    (*it).desactivar(true);

                }
            }

            it++;
        }


    } else {

        if (!nave->getImpactada()) {
            
            //condiciones de colision
            cond1 = abs(x - nave->getX() - IMPACT_RATIO)<(ancho / 2) || abs(x - nave->getX() + IMPACT_RATIO)<(ancho / 2);
            cond2 = abs(nave->getY() - y - SUELO) < alto;
            cond3 = abs(z - IMPACT_RATIO)<(profundidad / 2) || abs(z + IMPACT_RATIO)<(profundidad / 2);

            //colision nave contra edificio
            if (cond1 && cond2 && cond3) { 
                nave->addScore(-5);
                nave->setImpactada();
                alSourcePlay(source[0]);
              
            }

        }
        if (z >= ZLIM) { 
            //si  llega al principio de la pantalla reposicionar
            //y recalcular una nueva posicion
            randomSize();
            randomPos();
        }
    }
}
/**
 * Este metodo permite dibujar un edificio utilizando las texturas que
 * fueran especificadas al momento de la instanciacion.
 */
void Edificacion::dibujarEdificio() {

    GLfloat x_pos = ancho / 2;
    GLfloat z_pos = profundidad / 2;
    GLfloat y_pos = alto + SUELO;

    glBegin(GL_QUADS);
    glNormal3f(0.0, 0.0, 1.0);
    //frontal
    glTexCoord2f(0.0, 0.0);
    glVertex3f(-x_pos, SUELO, z_pos);

    glTexCoord2f(0.0, 1.0);
    glVertex3f(x_pos, SUELO, z_pos);

    glTexCoord2f(1.0, 1.0);
    glVertex3f(x_pos, y_pos, z_pos);

    glTexCoord2f(1.0, 0.0);
    glVertex3f(-x_pos, y_pos, z_pos);


    /** Si el edificio esta en el centro de la pantalla
     hacer desaparecer los lados izq, der y posterior*/
    if (z + z_pos <= 30 || abs(x) > 20) {
        //superior
        glNormal3f(0.0, 1.0, 0.0);

        glTexCoord2f(0.0, 0.0);
        glVertex3f(-x_pos, y_pos, z_pos);

        glTexCoord2f(0.0, 1.0);
        glVertex3f(x_pos, y_pos, z_pos);

        glTexCoord2f(1.0, 1.0);
        glVertex3f(x_pos, y_pos, -z_pos);

        glTexCoord2f(1.0, 0.0);
        glVertex3f(-x_pos, y_pos, -z_pos);


        glNormal3f(0.0, 0.0, -1.0);
        //posterior
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-x_pos, SUELO, -z_pos);

        glTexCoord2f(0.0, 1.0);
        glVertex3f(x_pos, SUELO, -z_pos);

        glTexCoord2f(1.0, 1.0);
        glVertex3f(x_pos, y_pos, -z_pos);

        glTexCoord2f(1.0, 0.0);
        glVertex3f(-x_pos, y_pos, -z_pos);



        glNormal3f(1.0, 0.0, 0.0);
        //lado der

        glTexCoord2f(0.0, 1.0);
        glVertex3f(x_pos, SUELO, z_pos);

        glTexCoord2f(0.0, 0.0);
        glVertex3f(x_pos, SUELO, -z_pos);

        glTexCoord2f(1.0, 0.0);
        glVertex3f(x_pos, y_pos, -z_pos);

        glTexCoord2f(1.0, 1.0);
        glVertex3f(x_pos, y_pos, z_pos);



        glNormal3f(-1.0, 0.0, 0.0);
        //lado izq

        glTexCoord2f(0.0, 1.0);
        glVertex3f(-x_pos, SUELO, z_pos);

        glTexCoord2f(0.0, 0.0);
        glVertex3f(-x_pos, SUELO, -z_pos);

        glTexCoord2f(1.0, 0.0);
        glVertex3f(-x_pos, y_pos, -z_pos);

        glTexCoord2f(1.0, 1.0);
        glVertex3f(-x_pos, y_pos, z_pos);
    }
    glEnd();

}

/**Este metodo permite dibujar una Edificacion sin consideras las texturas.
 * Se usa principalmente para dibujar las sombras.
 */
void Edificacion::dibujarEdificio_ST() {

    GLfloat x_pos = ancho / 2;
    GLfloat z_pos = profundidad / 2;
    GLfloat y_pos = alto + SUELO;

    glBegin(GL_QUADS);
    glNormal3f(0.0, 0.0, 1.0);
    //frontal

    glVertex3f(-x_pos, SUELO, z_pos);


    glVertex3f(x_pos, SUELO, z_pos);


    glVertex3f(x_pos, y_pos, z_pos);


    glVertex3f(-x_pos, y_pos, z_pos);

    glNormal3f(0.0, 0.0, -1.0);
    //posterior

    glVertex3f(-x_pos, SUELO, -z_pos);


    glVertex3f(x_pos, SUELO, -z_pos);


    glVertex3f(x_pos, y_pos, -z_pos);


    glVertex3f(-x_pos, y_pos, -z_pos);



    //superior
    glNormal3f(0.0, 1.0, 0.0);


    glVertex3f(-x_pos, y_pos, z_pos);


    glVertex3f(x_pos, y_pos, z_pos);


    glVertex3f(x_pos, y_pos, -z_pos);


    glVertex3f(-x_pos, y_pos, -z_pos);

    glNormal3f(1.0, 0.0, 0.0);
    //lado der


    glVertex3f(x_pos, SUELO, z_pos);


    glVertex3f(x_pos, SUELO, -z_pos);


    glVertex3f(x_pos, y_pos, -z_pos);


    glVertex3f(x_pos, y_pos, z_pos);



    glNormal3f(-1.0, 0.0, 0.0);
    //lado izq


    glVertex3f(-x_pos, SUELO, z_pos);


    glVertex3f(-x_pos, SUELO, -z_pos);


    glVertex3f(-x_pos, y_pos, -z_pos);


    glVertex3f(-x_pos, y_pos, z_pos);

    glEnd();

}

/**
 * Este metodo permite inicializar las dimensiones del edificio
 * de forma aleatoria.
 */
void Edificacion::randomSize() {

    srand(seed);
    seed++;
    ancho = 10.0 + rand() % 30;
    alto = 70.0 + rand() % 60;
    profundidad = 5.0 + rand() % 100;

}
/**
 * Este metodo permite colocar la edificación en una posicion aleatoria
 * en la escena.
 */
void Edificacion::randomPos() {
    srand(seed);
    seed++;
    x = rand() % (LIM_HORIZONTAL * 2) - LIM_HORIZONTAL; // -ancho < rx < ancho
    y = SUELO;
    z = ZINI + offset;

}

/**
 * Este metodo permite obtener la profundidad de la Edificacion.
 * @return La profundidad de la edificacion.
 */
GLfloat Edificacion::getProfundidad() {
    return profundidad;
}

/**
 * Este metodo permite empujar una edificacion una determinada cantidad de 
 * unidades desde su posicion actual.
 * 
 * @param units : La cantidad de unidades a desplazar.
 */
void Edificacion::empujarEdificio(GLfloat units) {
    z += units;

}


